// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import '../../constants/values.dart' show ConstantValue, PrimitiveConstantValue;
import '../../elements/entities.dart';
import '../../elements/names.dart';
import '../../elements/types.dart' show DartType;
import '../../js_model/js_world.dart';
import '../../serialization/serialization.dart';
import '../../universe/member_hierarchy.dart';
import '../../universe/record_shape.dart';
import '../../universe/selector.dart';
import '../../universe/world_builder.dart';
import '../../universe/use.dart';
import '../../world.dart';
import '../abstract_value_domain.dart';
import '../abstract_value_strategy.dart';
import 'powerset_bits.dart';

class PowersetValue implements AbstractValue {
  final AbstractValue _abstractValue;
  final int _powersetBits;
  PowersetValue(this._abstractValue, this._powersetBits);

  AbstractValue get abstractValue => _abstractValue;
  int get powersetBits => _powersetBits;

  @override
  bool operator ==(var other) {
    if (identical(this, other)) return true;
    if (other is! PowersetValue) return false;
    return _abstractValue == other._abstractValue &&
        _powersetBits == other._powersetBits;
  }

  @override
  int get hashCode {
    return _abstractValue.hashCode * _powersetBits.hashCode;
  }

  @override
  String toString() =>
      '${PowersetBitsDomain.toText(_powersetBits, omitIfTop: true)}'
      '$_abstractValue';
}

AbstractValue? unwrapOrNull(PowersetValue? powerset) {
  return powerset?._abstractValue;
}

PowersetValue? wrapOrNull(AbstractValue? abstractValue, int powersetBits) {
  return abstractValue == null
      ? null
      : PowersetValue(abstractValue, powersetBits);
}

class PowersetDomain with AbstractValueDomain {
  final AbstractValueDomain _abstractValueDomain;
  final PowersetBitsDomain _powersetBitsDomain;

  const PowersetDomain(this._abstractValueDomain, this._powersetBitsDomain);

  PowersetBitsDomain get powersetBitsDomain => _powersetBitsDomain;

  @override
  AbstractValue get internalTopType => PowersetValue(
    _abstractValueDomain.internalTopType,
    _powersetBitsDomain.internalTopType,
  );

  @override
  AbstractValue get dynamicType => PowersetValue(
    _abstractValueDomain.dynamicType,
    _powersetBitsDomain.dynamicType,
  );

  //TODO(coam)
  @override
  void writeAbstractValueToDataSink(
    DataSinkWriter sink,
    covariant PowersetValue value,
  ) {
    _abstractValueDomain.writeAbstractValueToDataSink(
      sink,
      value._abstractValue,
    );
  }

  //TODO(coam)
  @override
  AbstractValue readAbstractValueFromDataSource(DataSourceReader source) {
    int powersetBits = _powersetBitsDomain.powersetTop;
    AbstractValue abstractValue = _abstractValueDomain
        .readAbstractValueFromDataSource(source);
    return PowersetValue(abstractValue, powersetBits);
  }

  //TODO(coam)
  @override
  String getCompactText(covariant PowersetValue value) =>
      _abstractValueDomain.getCompactText(value._abstractValue);

  @override
  AbstractBool isFixedLengthJsIndexable(covariant PowersetValue value) =>
      _powersetBitsDomain.isOther(value._powersetBits).isDefinitelyFalse
          ? AbstractBool.false_
          : _abstractValueDomain.isFixedLengthJsIndexable(value._abstractValue);

  @override
  AbstractBool isJsIndexableAndIterable(covariant PowersetValue value) =>
      _powersetBitsDomain.isOther(value._powersetBits).isDefinitelyFalse
          ? AbstractBool.false_
          : _abstractValueDomain.isJsIndexableAndIterable(value._abstractValue);

  @override
  AbstractBool isJsIndexable(covariant PowersetValue value) =>
      _powersetBitsDomain.isOther(value._powersetBits).isDefinitelyFalse
          ? AbstractBool.false_
          : _abstractValueDomain.isJsIndexable(value._abstractValue);

  @override
  MemberEntity? locateSingleMember(
    covariant PowersetValue receiver,
    Selector selector,
  ) => _abstractValueDomain.locateSingleMember(
    receiver._abstractValue,
    selector,
  );

  @override
  AbstractBool isIn(
    covariant PowersetValue subset,
    covariant PowersetValue superset,
  ) => AbstractBool.strengthen(
    _powersetBitsDomain.isIn(subset._powersetBits, superset._powersetBits),
    _abstractValueDomain.isIn(subset._abstractValue, superset._abstractValue),
  );

  @override
  AbstractBool needsNoSuchMethodHandling(
    covariant PowersetValue receiver,
    Selector selector,
  ) => AbstractBool.strengthen(
    _powersetBitsDomain.needsNoSuchMethodHandling(
      receiver._powersetBits,
      selector,
    ),
    _abstractValueDomain.needsNoSuchMethodHandling(
      receiver._abstractValue,
      selector,
    ),
  );

  @override
  AbstractBool isTargetingMember(
    covariant PowersetValue receiver,
    MemberEntity member,
    Name name,
  ) => AbstractBool.strengthen(
    _powersetBitsDomain.isTargetingMember(receiver._powersetBits, member, name),
    _abstractValueDomain.isTargetingMember(
      receiver._abstractValue,
      member,
      name,
    ),
  );

  @override
  AbstractValue computeReceiver(Iterable<MemberEntity> members) {
    int powersetBits = _powersetBitsDomain.computeReceiver(members);
    AbstractValue abstractValue = _abstractValueDomain.computeReceiver(members);
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  PrimitiveConstantValue? getPrimitiveValue(covariant PowersetValue value) =>
      _powersetBitsDomain.getPrimitiveValue(value.powersetBits) ??
      _abstractValueDomain.getPrimitiveValue(value._abstractValue);

  @override
  AbstractValue createPrimitiveValue(
    covariant PowersetValue originalValue,
    PrimitiveConstantValue value,
  ) {
    int powersetBits = _powersetBitsDomain.createPrimitiveValue(value);
    AbstractValue abstractValue = _abstractValueDomain.createPrimitiveValue(
      originalValue._abstractValue,
      value,
    );
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  bool isPrimitiveValue(covariant PowersetValue value) =>
      _powersetBitsDomain.isPrimitiveValue(value.powersetBits) ||
      _abstractValueDomain.isPrimitiveValue(value._abstractValue);

  @override
  MemberEntity? getAllocationElement(covariant PowersetValue value) =>
      _abstractValueDomain.getAllocationElement(value._abstractValue);

  @override
  Object? getAllocationNode(covariant PowersetValue value) =>
      _abstractValueDomain.getAllocationNode(value._abstractValue);

  @override
  AbstractValue getGeneralization(covariant PowersetValue value) {
    int powersetBits = _powersetBitsDomain.powersetTop;
    final abstractValue =
        _abstractValueDomain.getGeneralization(unwrapOrNull(value))!;
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  bool isSpecializationOf(
    covariant PowersetValue specialization,
    covariant PowersetValue generalization,
  ) => _abstractValueDomain.isSpecializationOf(
    specialization._abstractValue,
    generalization._abstractValue,
  );

  @override
  AbstractValue getDictionaryValueForKey(
    covariant PowersetValue value,
    String key,
  ) {
    if (_powersetBitsDomain.isOther(value._powersetBits).isDefinitelyFalse) {
      return dynamicType;
    }
    AbstractValue abstractValue = _abstractValueDomain.getDictionaryValueForKey(
      value._abstractValue,
      key,
    );
    return PowersetValue(abstractValue, _powersetBitsDomain.powersetTop);
  }

  @override
  bool containsDictionaryKey(covariant PowersetValue value, String key) =>
      _powersetBitsDomain.isOther(value._powersetBits).isPotentiallyTrue &&
      _abstractValueDomain.containsDictionaryKey(value._abstractValue, key);

  @override
  AbstractValue createDictionaryValue(
    covariant PowersetValue originalValue,
    Object? allocationNode,
    MemberEntity? allocationElement,
    covariant PowersetValue key,
    covariant PowersetValue value,
    covariant Map<String, AbstractValue> mappings,
  ) {
    final powersetBits = originalValue._powersetBits;
    AbstractValue abstractValue = _abstractValueDomain.createDictionaryValue(
      originalValue._abstractValue,
      allocationNode,
      allocationElement,
      key._abstractValue,
      value._abstractValue,
      {
        for (var entry in mappings.entries)
          entry.key: (entry.value as PowersetValue)._abstractValue,
      },
    );
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  bool isDictionary(covariant PowersetValue value) =>
      _powersetBitsDomain.isOther(value._powersetBits).isPotentiallyTrue &&
      _abstractValueDomain.isDictionary(value._abstractValue);

  @override
  AbstractValue createRecordValue(
    RecordShape shape,
    List<AbstractValue> types,
  ) {
    AbstractValue abstractValue = _abstractValueDomain.createRecordValue(
      shape,
      types
          .map((e) => (e as PowersetValue)._abstractValue)
          .toList(growable: false),
    );
    return PowersetValue(abstractValue, _powersetBitsDomain.powersetTop);
  }

  @override
  bool isRecord(covariant PowersetValue value) =>
      _powersetBitsDomain.isOther(value._powersetBits).isPotentiallyTrue &&
      _abstractValueDomain.isRecord(value._abstractValue);

  @override
  bool recordHasGetter(covariant PowersetValue value, String getterName) =>
      _abstractValueDomain.recordHasGetter(value._abstractValue, getterName);

  @override
  AbstractValue getGetterTypeInRecord(
    covariant PowersetValue value,
    String getterName,
  ) => _abstractValueDomain.getGetterTypeInRecord(
    value._abstractValue,
    getterName,
  );

  @override
  AbstractValue getMapValueType(covariant PowersetValue value) {
    if (_powersetBitsDomain.isOther(value._powersetBits).isDefinitelyFalse) {
      return dynamicType;
    }
    AbstractValue abstractValue = _abstractValueDomain.getMapValueType(
      value._abstractValue,
    );
    return PowersetValue(abstractValue, _powersetBitsDomain.powersetTop);
  }

  @override
  AbstractValue getMapKeyType(covariant PowersetValue value) {
    if (_powersetBitsDomain.isOther(value._powersetBits).isDefinitelyFalse) {
      return dynamicType;
    }
    AbstractValue abstractValue = _abstractValueDomain.getMapValueType(
      value._abstractValue,
    );
    return PowersetValue(abstractValue, _powersetBitsDomain.powersetTop);
  }

  @override
  AbstractValue createMapValue(
    covariant PowersetValue originalValue,
    Object? allocationNode,
    MemberEntity? allocationElement,
    covariant PowersetValue key,
    covariant PowersetValue value,
  ) {
    int powersetBits = originalValue._powersetBits;
    AbstractValue abstractValue = _abstractValueDomain.createMapValue(
      originalValue._abstractValue,
      allocationNode,
      allocationElement,
      key._abstractValue,
      value._abstractValue,
    );
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  bool isMap(covariant PowersetValue value) =>
      _powersetBitsDomain.isOther(value._powersetBits).isPotentiallyTrue &&
      _abstractValueDomain.isMap(value._abstractValue);

  @override
  AbstractValue getSetElementType(covariant PowersetValue value) {
    if (_powersetBitsDomain.isOther(value._powersetBits).isDefinitelyFalse) {
      return dynamicType;
    }
    AbstractValue abstractValue = _abstractValueDomain.getSetElementType(
      value._abstractValue,
    );
    return PowersetValue(abstractValue, _powersetBitsDomain.powersetTop);
  }

  @override
  AbstractValue createSetValue(
    covariant PowersetValue originalValue,
    Object? allocationNode,
    MemberEntity? allocationElement,
    covariant PowersetValue elementType,
  ) {
    int powersetBits = originalValue._powersetBits;
    AbstractValue abstractValue = _abstractValueDomain.createSetValue(
      originalValue._abstractValue,
      allocationNode,
      allocationElement,
      elementType._abstractValue,
    );
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  bool isSet(covariant PowersetValue value) =>
      _powersetBitsDomain.isOther(value._powersetBits).isPotentiallyTrue &&
      _abstractValueDomain.isSet(value._abstractValue);

  @override
  int? getContainerLength(covariant PowersetValue value) =>
      _powersetBitsDomain.isOther(value._powersetBits).isDefinitelyFalse
          ? null
          : _abstractValueDomain.getContainerLength(value._abstractValue);

  @override
  AbstractValue getContainerElementType(covariant PowersetValue value) {
    if (_powersetBitsDomain.isOther(value._powersetBits).isDefinitelyFalse) {
      return dynamicType;
    }
    AbstractValue abstractValue = _abstractValueDomain.getContainerElementType(
      value._abstractValue,
    );
    return PowersetValue(abstractValue, _powersetBitsDomain.powersetTop);
  }

  @override
  AbstractValue createContainerValue(
    covariant PowersetValue originalValue,
    Object? allocationNode,
    MemberEntity? allocationElement,
    covariant PowersetValue elementType,
    int? length,
  ) {
    int powersetBits = originalValue._powersetBits;
    AbstractValue abstractValue = _abstractValueDomain.createContainerValue(
      originalValue._abstractValue,
      allocationNode,
      allocationElement,
      elementType._abstractValue,
      length,
    );
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  bool isContainer(covariant PowersetValue value) =>
      _powersetBitsDomain.isOther(value._powersetBits).isPotentiallyTrue &&
      _abstractValueDomain.isContainer(value._abstractValue);

  // TODO(coam): this can be more precise if we build a ConstantValue visitor
  // that can tell us information about the bits given a ConstantValue
  @override
  AbstractValue computeAbstractValueForConstant(covariant ConstantValue value) {
    int powersetBits = _powersetBitsDomain.computeAbstractValueForConstant(
      value,
    );
    AbstractValue abstractValue = _abstractValueDomain
        .computeAbstractValueForConstant(value);
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  AbstractValue? getAbstractValueForNativeMethodParameterType(DartType type) {
    int powersetBits = _powersetBitsDomain.powersetTop;
    final abstractValue = _abstractValueDomain
        .getAbstractValueForNativeMethodParameterType(type);
    return wrapOrNull(abstractValue, powersetBits);
  }

  @override
  AbstractBool containsAll(covariant PowersetValue a) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.containsAll(a._powersetBits),
        _abstractValueDomain.containsAll(a._abstractValue),
      );

  @override
  AbstractBool areDisjoint(
    covariant PowersetValue a,
    covariant PowersetValue b,
  ) => AbstractBool.strengthen(
    _powersetBitsDomain.areDisjoint(a._powersetBits, b._powersetBits),
    _abstractValueDomain.areDisjoint(a._abstractValue, b._abstractValue),
  );

  @override
  AbstractValue intersection(
    covariant PowersetValue a,
    covariant PowersetValue b,
  ) {
    int powersetBits = _powersetBitsDomain.intersection(
      a._powersetBits,
      b._powersetBits,
    );
    AbstractValue abstractValue = _abstractValueDomain.intersection(
      a._abstractValue,
      b._abstractValue,
    );
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  AbstractValue unionOfMany(Iterable<AbstractValue> values) {
    PowersetValue result = PowersetValue(
      _abstractValueDomain.emptyType,
      _powersetBitsDomain.powersetBottom,
    );
    for (final value in values) {
      result = union(result, value as PowersetValue);
    }
    return result;
  }

  @override
  PowersetValue union(covariant PowersetValue a, covariant PowersetValue b) {
    int powersetBits = _powersetBitsDomain.union(
      a._powersetBits,
      b._powersetBits,
    );
    AbstractValue abstractValue = _abstractValueDomain.union(
      a._abstractValue,
      b._abstractValue,
    );
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  AbstractBool isPrimitiveOrNull(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isPrimitiveOrNull(value._powersetBits),
        _abstractValueDomain.isPrimitiveOrNull(value._abstractValue),
      );

  @override
  AbstractBool isStringOrNull(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isStringOrNull(value._powersetBits),
        _abstractValueDomain.isStringOrNull(value._abstractValue),
      );

  @override
  AbstractBool isString(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isString(value._powersetBits),
        _abstractValueDomain.isString(value._abstractValue),
      );

  @override
  AbstractBool isBooleanOrNull(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isBooleanOrNull(value._powersetBits),
        _abstractValueDomain.isBooleanOrNull(value._abstractValue),
      );

  @override
  AbstractBool isBoolean(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isBoolean(value._powersetBits),
        _abstractValueDomain.isBoolean(value._abstractValue),
      );

  @override
  AbstractBool isTruthy(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isTruthy(value._powersetBits),
        _abstractValueDomain.isTruthy(value._abstractValue),
      );

  @override
  AbstractBool isNumberOrNull(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isNumberOrNull(value._powersetBits),
        _abstractValueDomain.isNumberOrNull(value._abstractValue),
      );

  @override
  AbstractBool isNumber(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isNumber(value._powersetBits),
        _abstractValueDomain.isNumber(value._abstractValue),
      );

  @override
  AbstractBool isIntegerOrNull(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isIntegerOrNull(value._powersetBits),
        _abstractValueDomain.isIntegerOrNull(value._abstractValue),
      );

  @override
  AbstractBool isPositiveIntegerOrNull(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isPositiveIntegerOrNull(value._powersetBits),
        _abstractValueDomain.isPositiveIntegerOrNull(value._abstractValue),
      );

  @override
  AbstractBool isPositiveInteger(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isPositiveInteger(value._powersetBits),
        _abstractValueDomain.isPositiveInteger(value._abstractValue),
      );

  @override
  AbstractBool isUInt31(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isUInt31(value._powersetBits),
        _abstractValueDomain.isUInt31(value._abstractValue),
      );

  @override
  AbstractBool isUInt32(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isUInt32(value._powersetBits),
        _abstractValueDomain.isUInt32(value._abstractValue),
      );

  @override
  AbstractBool isInteger(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isInteger(value._powersetBits),
        _abstractValueDomain.isInteger(value._abstractValue),
      );

  @override
  AbstractBool isInterceptor(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isInterceptor(value._powersetBits),
        _abstractValueDomain.isInterceptor(value._abstractValue),
      );

  @override
  AbstractBool isPrimitiveString(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isPrimitiveString(value._powersetBits),
        _abstractValueDomain.isPrimitiveString(value._abstractValue),
      );

  @override
  AbstractBool isArray(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isArray(value._powersetBits),
        _abstractValueDomain.isArray(value._abstractValue),
      );

  @override
  AbstractBool isMutableIndexable(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isMutableIndexable(value._powersetBits),
        _abstractValueDomain.isMutableIndexable(value._abstractValue),
      );

  @override
  AbstractBool isMutableArray(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isMutableArray(value._powersetBits),
        _abstractValueDomain.isMutableArray(value._abstractValue),
      );

  @override
  AbstractBool isExtendableArray(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isExtendableArray(value._powersetBits),
        _abstractValueDomain.isExtendableArray(value._abstractValue),
      );

  @override
  AbstractBool isFixedArray(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isFixedArray(value._powersetBits),
        _abstractValueDomain.isFixedArray(value._abstractValue),
      );

  @override
  AbstractBool isIndexablePrimitive(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isIndexablePrimitive(value._powersetBits),
        _abstractValueDomain.isIndexablePrimitive(value._abstractValue),
      );

  @override
  AbstractBool isPrimitiveBoolean(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isPrimitiveBoolean(value._powersetBits),
        _abstractValueDomain.isPrimitiveBoolean(value._abstractValue),
      );

  @override
  AbstractBool isPrimitiveNumber(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isPrimitiveNumber(value._powersetBits),
        _abstractValueDomain.isPrimitiveNumber(value._abstractValue),
      );

  @override
  AbstractBool isPrimitive(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isPrimitive(value._powersetBits),
        _abstractValueDomain.isPrimitive(value._abstractValue),
      );

  @override
  AbstractBool isNull(covariant PowersetValue value) => AbstractBool.strengthen(
    _powersetBitsDomain.isNull(value._powersetBits),
    _abstractValueDomain.isNull(value._abstractValue),
  );

  @override
  AbstractBool isLateSentinel(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isLateSentinel(value._powersetBits),
        _abstractValueDomain.isLateSentinel(value._abstractValue),
      );

  @override
  ClassEntity? getExactClass(covariant PowersetValue value) =>
      _abstractValueDomain.getExactClass(value._abstractValue);

  @override
  AbstractBool isExact(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isExact(value._powersetBits),
        _abstractValueDomain.isExact(value._abstractValue),
      );

  @override
  AbstractBool isEmpty(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isEmpty(value._powersetBits),
        _abstractValueDomain.isEmpty(value._abstractValue),
      );

  @override
  AbstractBool isInstanceOf(covariant PowersetValue value, ClassEntity cls) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isInstanceOf(value._powersetBits, cls),
        _abstractValueDomain.isInstanceOf(value._abstractValue, cls),
      );

  @override
  AbstractBool containsOnlyType(
    covariant PowersetValue value,
    ClassEntity cls,
  ) => AbstractBool.strengthen(
    _powersetBitsDomain.containsOnlyType(value._powersetBits, cls),
    _abstractValueDomain.containsOnlyType(value._abstractValue, cls),
  );

  @override
  AbstractBool containsType(covariant PowersetValue value, ClassEntity cls) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.containsType(value._powersetBits, cls),
        _abstractValueDomain.containsType(value._abstractValue, cls),
      );

  @override
  AbstractValue includeNull(covariant PowersetValue value) {
    int powersetBits = _powersetBitsDomain.includeNull(value._powersetBits);
    AbstractValue abstractValue = _abstractValueDomain.includeNull(
      value._abstractValue,
    );
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  AbstractValue excludeNull(covariant PowersetValue value) {
    int powersetBits = _powersetBitsDomain.excludeNull(value._powersetBits);
    AbstractValue abstractValue = _abstractValueDomain.excludeNull(
      value._abstractValue,
    );
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  AbstractValue includeLateSentinel(covariant PowersetValue value) {
    int powersetBits = _powersetBitsDomain.includeLateSentinel(
      value._powersetBits,
    );
    AbstractValue abstractValue = _abstractValueDomain.includeLateSentinel(
      value._abstractValue,
    );
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  AbstractValue excludeLateSentinel(covariant PowersetValue value) {
    int powersetBits = _powersetBitsDomain.excludeLateSentinel(
      value._powersetBits,
    );
    AbstractValue abstractValue = _abstractValueDomain.excludeLateSentinel(
      value._abstractValue,
    );
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  AbstractBool couldBeTypedArray(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.couldBeTypedArray(value._powersetBits),
        _abstractValueDomain.couldBeTypedArray(value._abstractValue),
      );

  @override
  AbstractBool isTypedArray(covariant PowersetValue value) =>
      AbstractBool.strengthen(
        _powersetBitsDomain.isTypedArray(value._powersetBits),
        _abstractValueDomain.isTypedArray(value._abstractValue),
      );

  @override
  AbstractValue createNullableSubtype(ClassEntity cls) {
    int powersetBits = _powersetBitsDomain.createNullableSubtype(cls);
    AbstractValue abstractValue = _abstractValueDomain.createNullableSubtype(
      cls,
    );
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  AbstractValue createNonNullSubtype(ClassEntity cls) {
    int powersetBits = _powersetBitsDomain.createNonNullSubtype(cls);
    AbstractValue abstractValue = _abstractValueDomain.createNonNullSubtype(
      cls,
    );
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  AbstractValue createNonNullSubclass(ClassEntity cls) {
    int powersetBits = _powersetBitsDomain.createNonNullSubclass(cls);
    AbstractValue abstractValue = _abstractValueDomain.createNonNullSubclass(
      cls,
    );
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  AbstractValue createNullableExact(ClassEntity cls) {
    int powersetBits = _powersetBitsDomain.createNullableExact(cls);
    AbstractValue abstractValue = _abstractValueDomain.createNullableExact(cls);
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  AbstractValue createNonNullExact(ClassEntity cls) {
    int powersetBits = _powersetBitsDomain.createNonNullExact(cls);
    AbstractValue abstractValue = _abstractValueDomain.createNonNullExact(cls);
    return PowersetValue(abstractValue, powersetBits);
  }

  @override
  AbstractValueWithPrecision createFromStaticType(DartType type) {
    int powersetBits = _powersetBitsDomain.createFromStaticType(type);
    var unwrapped = _abstractValueDomain.createFromStaticType(type);
    return AbstractValueWithPrecision(
      PowersetValue(unwrapped.abstractValue, powersetBits),
      unwrapped.isPrecise,
    );
  }

  @override
  AbstractValue get asyncStarStreamType => PowersetValue(
    _abstractValueDomain.asyncStarStreamType,
    _powersetBitsDomain.asyncStarStreamType,
  );

  @override
  AbstractValue get asyncFutureType => PowersetValue(
    _abstractValueDomain.asyncFutureType,
    _powersetBitsDomain.asyncFutureType,
  );

  @override
  AbstractValue get syncStarIterableType => PowersetValue(
    _abstractValueDomain.syncStarIterableType,
    _powersetBitsDomain.syncStarIterableType,
  );

  @override
  AbstractValue get emptyType => PowersetValue(
    _abstractValueDomain.emptyType,
    _powersetBitsDomain.emptyType,
  );

  @override
  AbstractValue get constMapType => PowersetValue(
    _abstractValueDomain.constMapType,
    _powersetBitsDomain.constMapType,
  );

  @override
  AbstractValue get constSetType => PowersetValue(
    _abstractValueDomain.constSetType,
    _powersetBitsDomain.constSetType,
  );

  @override
  AbstractValue get constListType => PowersetValue(
    _abstractValueDomain.constListType,
    _powersetBitsDomain.constListType,
  );

  @override
  AbstractValue get positiveIntType => PowersetValue(
    _abstractValueDomain.positiveIntType,
    _powersetBitsDomain.positiveIntType,
  );

  @override
  AbstractValue get uint32Type => PowersetValue(
    _abstractValueDomain.uint32Type,
    _powersetBitsDomain.uint32Type,
  );

  @override
  AbstractValue get uint31Type => PowersetValue(
    _abstractValueDomain.uint31Type,
    _powersetBitsDomain.uint31Type,
  );

  @override
  AbstractValue get fixedListType => PowersetValue(
    _abstractValueDomain.fixedListType,
    _powersetBitsDomain.fixedListType,
  );

  @override
  AbstractValue get growableListType => PowersetValue(
    _abstractValueDomain.growableListType,
    _powersetBitsDomain.growableListType,
  );

  @override
  AbstractValue get mutableArrayType => PowersetValue(
    _abstractValueDomain.mutableArrayType,
    _powersetBitsDomain.mutableArrayType,
  );

  @override
  AbstractValue get nullType => PowersetValue(
    _abstractValueDomain.nullType,
    _powersetBitsDomain.nullType,
  );

  @override
  AbstractValue get nonNullType => PowersetValue(
    _abstractValueDomain.nonNullType,
    _powersetBitsDomain.nonNullType,
  );

  @override
  AbstractValue get lateSentinelType => PowersetValue(
    _abstractValueDomain.lateSentinelType,
    _powersetBitsDomain.lateSentinelType,
  );

  @override
  AbstractValue get mapType =>
      PowersetValue(_abstractValueDomain.mapType, _powersetBitsDomain.mapType);

  @override
  AbstractValue get setType =>
      PowersetValue(_abstractValueDomain.setType, _powersetBitsDomain.setType);

  @override
  AbstractValue get listType => PowersetValue(
    _abstractValueDomain.listType,
    _powersetBitsDomain.listType,
  );

  @override
  AbstractValue get stringType => PowersetValue(
    _abstractValueDomain.stringType,
    _powersetBitsDomain.stringType,
  );

  @override
  AbstractValue get numType =>
      PowersetValue(_abstractValueDomain.numType, _powersetBitsDomain.numType);

  @override
  AbstractValue get numNotIntType => PowersetValue(
    _abstractValueDomain.numNotIntType,
    _powersetBitsDomain.numNotIntType,
  );

  @override
  AbstractValue get intType =>
      PowersetValue(_abstractValueDomain.intType, _powersetBitsDomain.intType);

  @override
  AbstractValue get boolType => PowersetValue(
    _abstractValueDomain.boolType,
    _powersetBitsDomain.boolType,
  );

  @override
  AbstractValue get functionType => PowersetValue(
    _abstractValueDomain.functionType,
    _powersetBitsDomain.functionType,
  );

  @override
  AbstractValue get recordType => PowersetValue(
    _abstractValueDomain.recordType,
    _powersetBitsDomain.recordType,
  );

  @override
  AbstractValue get typeType => PowersetValue(
    _abstractValueDomain.typeType,
    _powersetBitsDomain.typeType,
  );

  @override
  Iterable<DynamicCallTarget> findRootsOfTargets(
    covariant PowersetValue receiver,
    Selector selector,
    MemberHierarchyBuilder memberHierarchyBuilder,
  ) => _abstractValueDomain.findRootsOfTargets(
    receiver.abstractValue,
    selector,
    memberHierarchyBuilder,
  );

  @override
  bool isInvalidRefinement(
    covariant PowersetValue before,
    covariant PowersetValue after,
  ) {
    return _abstractValueDomain.isInvalidRefinement(
      before._abstractValue,
      after._abstractValue,
    );
  }
}

class PowersetStrategy implements AbstractValueStrategy<PowersetDomain> {
  final AbstractValueStrategy _abstractValueStrategy;
  const PowersetStrategy(this._abstractValueStrategy);

  @override
  PowersetDomain createDomain(JClosedWorld closedWorld) {
    return PowersetDomain(
      _abstractValueStrategy.createDomain(closedWorld),
      PowersetBitsDomain(closedWorld),
    );
  }

  @override
  SelectorConstraintsStrategy createSelectorStrategy(PowersetDomain domain) {
    return PowersetsSelectorStrategy(
      _abstractValueStrategy.createSelectorStrategy(
        domain._abstractValueDomain,
      ),
    );
  }
}

class PowersetsSelectorStrategy implements SelectorConstraintsStrategy {
  final SelectorConstraintsStrategy _selectorConstraintsStrategy;
  const PowersetsSelectorStrategy(this._selectorConstraintsStrategy);

  @override
  UniverseSelectorConstraints createSelectorConstraints(
    Selector selector,
    Object? initialConstraint,
  ) {
    return PowersetsUniverseSelectorConstraints(
      _selectorConstraintsStrategy.createSelectorConstraints(
        selector,
        initialConstraint == null
            ? null
            : (initialConstraint as PowersetValue)._abstractValue,
      ),
    );
  }

  @override
  bool appliedUnnamed(
    DynamicUse dynamicUse,
    MemberEntity member,
    covariant JClosedWorld world,
  ) {
    return _selectorConstraintsStrategy.appliedUnnamed(
      dynamicUse.withReceiverConstraint(
        unwrapOrNull(dynamicUse.receiverConstraint as PowersetValue?),
      ),
      member,
      world,
    );
  }
}

class PowersetsUniverseSelectorConstraints
    implements UniverseSelectorConstraints {
  final UniverseSelectorConstraints _universeSelectorConstraints;
  const PowersetsUniverseSelectorConstraints(this._universeSelectorConstraints);

  @override
  bool addReceiverConstraint(Object? constraint) =>
      _universeSelectorConstraints.addReceiverConstraint(
        constraint == null
            ? null
            : (constraint as PowersetValue)._abstractValue,
      );

  @override
  bool needsNoSuchMethodHandling(Selector selector, World world) =>
      _universeSelectorConstraints.needsNoSuchMethodHandling(selector, world);

  @override
  bool canHit(MemberEntity element, Name name, World world) =>
      _universeSelectorConstraints.canHit(element, name, world);

  @override
  String toString() => 'PowersetsUniverseSelectorConstraints:$hashCode';
}
